//========================================================================
// *作者：海盗猫  主页：ilplay.com
// *脚本：DrawScriptTamp
// *描述：C#代码构建Editor脚本
// *?2021 MYJL . All rights reserved.
//* ======================================================================
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using HDMCode.UIFrame;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using Component = UnityEngine.Component;

namespace HDMCode.UIFrameEditor
{
	public static class DrawScriptTemplate
    {
        public static readonly string ExtendName = "UIDisplay";
        public static readonly string ScriptName = "";
        public static readonly string NameSpacesName = "HDMCode.UIFrame";
        public static readonly string FieldPrefix = "Auto";
        public static readonly string AssetsPrefix = "";
        public static readonly string PartialClassName = "UIAssets";
        public static readonly string LoadUIFuncionName = "BindUIBehavior";
        public static readonly string SavePath = $"/Scripts/Generate/";
        private static string m_ClassName;

        /// <summary>
        /// 创建默认脚本
        /// </summary>
        /// <param name="gameObjectName">对象名称</param>
        /// <param name="assetsName">创建资源名称</param>
        /// <param name="baseName">继承名称</param>
        /// <param name="outPath">输出路径</param>
        /// <param name="elements">组件</param>
        public static void CreatScript(string gameObjectName,string assetsName, string baseName,string outPath, List<UIComponentElement> elements ) 
		{
            m_ClassName = $"HDMAuto{assetsName}";
            Type type = Assembly.GetAssembly(typeof(UIDisplay)).GetType($"{NameSpacesName}.{m_ClassName}");

            if (type != null)
            {
                if (!EditorUtility.DisplayDialog("错误", "是否更新并重写生成UI管理脚本！", "更新","取消") )
                {
                    return;
                }
            }

            //生成代码设置
            CodeCompileUnit script = new CodeCompileUnit();
            var scriptNameSpace = CreatNameSpace(script);
            var scriptClass = CreatClass(scriptNameSpace, m_ClassName, baseName);
            CreatCode(scriptClass, elements);

            var scriptPartialClass = CreatPartialClass(scriptNameSpace);
            CreatPartialCode(scriptPartialClass, assetsName, gameObjectName);

            //代码风格设置
            CodeGeneratorOptions options = new CodeGeneratorOptions();
            options.BracingStyle = "C";
            options.BlankLinesBetweenMembers = true;

            string outputFile = $"{outPath}/{m_ClassName}.cs";
            CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");

            //生成代码文件
            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(outputFile))
            {
                provider.GenerateCodeFromCompileUnit(script, sw, options);
            }
            AssetDatabase.Refresh();
        }


        /// <summary>
        /// 创建命名空间
        /// </summary>
        /// <returns></returns>
        public static CodeNamespace CreatNameSpace(CodeCompileUnit script) 
		{
            CodeNamespace scriptNamespace = new CodeNamespace("HDMCode.UIFrame");
            scriptNamespace.Imports.Add(new CodeNamespaceImport("UnityEngine"));
            scriptNamespace.Imports.Add(new CodeNamespaceImport("UnityEngine.UI"));
            script.Namespaces.Add(scriptNamespace);
            return scriptNamespace;
        }

        /// <summary>
        /// 创建主类
        /// </summary>
        public static CodeTypeDeclaration CreatClass(CodeNamespace scriptNameSpace, string className, string baseName)
        {
            CodeTypeDeclaration scriptClass = new CodeTypeDeclaration(className);
            scriptClass.IsClass = true;
            scriptClass.TypeAttributes = TypeAttributes.Public;
            scriptClass.BaseTypes.Add(baseName);
            scriptNameSpace.Types.Add(scriptClass);
            return scriptClass;
        }


        /// <summary>
        /// 创建代码
        /// </summary>
        public static void CreatCode(CodeTypeDeclaration sctiptClass, List<UIComponentElement> elements)
        {
            if (elements == null)
                return;

            List<CodeVariableDeclarationStatement> fileds = new List<CodeVariableDeclarationStatement>();
            
            //绘制自定义字段
            for (int i = 0; i < elements.Count; i++)
            {
                GameObject element = elements[i].target;
                for (int k = 0; k < elements[i].componentList.Count ; k++)
                {
                    Component component = elements[i].componentList[k];

                    string fieldName = $"{FormatSting(element.name)}_{FormatSting(component.GetType().Name)}";

                    //写入字段
                    CodeMemberField field = new CodeMemberField(component.GetType(), fieldName );
                    field.Attributes = MemberAttributes.Public;
                    sctiptClass.Members.Add(field);

                    CodeCastExpression changeFun = new CodeCastExpression();
                    CodeTypeReference type = new CodeTypeReference(component.GetType());
                    CodeVariableReferenceExpression value = new CodeVariableReferenceExpression();
                    value.VariableName = $"components[{i}].componentList[{k}]";
                    changeFun.TargetType = type;
                    changeFun.Expression = value;
                    CodeVariableDeclarationStatement funtion = new CodeVariableDeclarationStatement(" ",fieldName , changeFun );
                    fileds.Add(funtion);
                }
            }

            CodeMemberField animatorField = new CodeMemberField(typeof(Animator), "_animator");
            animatorField.Attributes = MemberAttributes.Private;
            sctiptClass.Members.Add(animatorField);

            CodeMemberProperty animatorProperty = new CodeMemberProperty();
            animatorProperty.Name = "animator";
            animatorProperty.Type = new CodeTypeReference(typeof(Animator));
            // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
            animatorProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            animatorProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_animator")));
            animatorProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_animator"), new CodePropertySetValueReferenceExpression()));
            sctiptClass.Members.Add(animatorProperty);


            CodeMemberField audioField = new CodeMemberField(typeof(AudioSource), "_audio");
            animatorField.Attributes = MemberAttributes.Private;
            sctiptClass.Members.Add(audioField);

            CodeMemberProperty audioProperty = new CodeMemberProperty();
            audioProperty.Name = "audio";
            audioProperty.Type = new CodeTypeReference(typeof(AudioSource));
            // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
            audioProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            audioProperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_audio")));
            audioProperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_audio"), new CodePropertySetValueReferenceExpression()));
            sctiptClass.Members.Add(audioProperty);


            //绘制获取组件方法
            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = "SetComponents";
            method.Attributes = MemberAttributes.Public;
            method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(System.Collections.Generic.List<UIComponentElement>), "components"));
            //method.Statements.Add(initFun);
            // CodeMethodReferenceExpression codeMethod = new CodeMethodReferenceExpression();
            // codeMethod.MethodName = "HDMUI.UI.GetUIComponents()";
            // CodeVariableDeclarationStatement initFun = new CodeVariableDeclarationStatement(typeof(List<UIComponentElement>), "components", codeMethod );

            List<CodeStatement> funtions = new List<CodeStatement>();
            foreach (var item in fileds)
            {
                funtions.Add(item);
            }
            CodeConditionStatement conditionalStatement = new CodeConditionStatement(new CodeVariableReferenceExpression("components!=null"),funtions.ToArray());
            method.Statements.Add(conditionalStatement);
            sctiptClass.Members.Add(method);
        }


        /// <summary>
        /// 创建方法
        /// </summary>
        /// <returns></returns>
        public static Dictionary<Type, CodeMemberMethod> GetMethod()
        {
            Dictionary<Type, CodeMemberMethod> creatMethod = new Dictionary<Type, CodeMemberMethod>()
            {
                { typeof(Button), Method("OnButtonChange",new[]{ typeof(Button)})},
                { typeof(Toggle), Method("OnToggleChange",new[]{ typeof(Toggle),typeof(bool) })},
                { typeof(Slider), Method("OnSliderChange",new[]{ typeof(Slider),typeof(float) })},
                { typeof(Scrollbar), Method("OnScrollbarChange",new[]{ typeof(Scrollbar),typeof(float)})},
                { typeof(InputField), Method("OnInputFieldChange",new[]{ typeof(InputField),typeof(string)})},
            };

            CodeMemberMethod Method(string functionName, Type[] parameter)
            {
                CodeMemberMethod method = new CodeMemberMethod();
                method.Name = functionName;
                // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
                method.Attributes = MemberAttributes.Override | MemberAttributes.Public;
                foreach (Type type in parameter)
                {
                    method.Parameters.Add(new CodeParameterDeclarationExpression(type, type.Name));
                }
                return method;
            }
            return creatMethod;
        }


        /// <summary>
        /// 创建资源部分类
        /// </summary>
        public static CodeTypeDeclaration CreatPartialClass(CodeNamespace scriptNameSpace)
        {
            CodeTypeDeclaration scriptClass = new CodeTypeDeclaration($"{PartialClassName}");
            scriptClass.IsClass = true;
            scriptClass.IsPartial = true;
            scriptClass.Attributes = MemberAttributes.Static;
            scriptClass.TypeAttributes = TypeAttributes.Public;

            scriptNameSpace.Types.Add(scriptClass);
            return scriptClass;
        }


        /// <summary>
        /// 创建部分类字段
        /// </summary>
        /// <param name="sctiptClass"></param>
        /// <param name="assetsName"></param>
        /// <param name="gameobjectName"></param>
        public static void CreatPartialCode(CodeTypeDeclaration sctiptClass,string assetsName, string gameobjectName)
        {
            string lowerFiledName = $"{AssetsPrefix}{assetsName}";
            string upperFiledName = $"{lowerFiledName.Substring(0,1).ToUpper()}{lowerFiledName.Substring(1, lowerFiledName.Length-1)}";
            CreatPropertyCode(sctiptClass, upperFiledName, lowerFiledName , assetsName, gameobjectName);
        }


        /// <summary>
        /// 创建属性字段
        /// </summary>
        /// <param name="sctiptClass"></param>
        /// <param name="propertyName"></param>
        /// <param name="filedName"></param>
        /// <param name="assetsName"></param>
        /// <param name="gameobjectName"></param>
        public static void CreatPropertyCode(CodeTypeDeclaration sctiptClass, string propertyName,string filedName,string assetsName, string gameobjectName)
        {

            CodeMemberField fd = new CodeMemberField(m_ClassName, $"_{m_ClassName.ToLower()}");
            // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
            fd.Attributes = MemberAttributes.Private | MemberAttributes.Static;
            sctiptClass.Members.Add(fd);

            CodeMemberProperty property = new CodeMemberProperty();
            property.Name = propertyName;
            property.Type = new CodeTypeReference($"(HDMCode.UIFrame.UIEntity native, HDMCode.UIFrame.{m_ClassName} append)");
            // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
            property.Attributes = MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
            //添加方法
            CodeMethodReferenceExpression codeMethod = new CodeMethodReferenceExpression();
            codeMethod.MethodName = "HDMUI.UI.AddToUIBehavior";
            codeMethod.TypeArguments.Add($"HDMCode.UIFrame.{m_ClassName}");

            CodeParameterDeclarationExpression valueA = new CodeParameterDeclarationExpression(" ",$"ref _{m_ClassName.ToLower()}");
            CodeVariableReferenceExpression valueB = new CodeVariableReferenceExpression($"\"{gameobjectName}\"");
            CodeMethodInvokeExpression method = new CodeMethodInvokeExpression(codeMethod , new CodeExpression[] { valueA, valueB });
            CodeMethodReturnStatement setReturn = new CodeMethodReturnStatement(method);
            property.GetStatements.Add(setReturn);
            sctiptClass.Members.Add(property);
        }

        /// <summary>
        /// 通过正则表达式规范名称
        /// </summary>
        /// <param name="strings"></param>
        /// <returns></returns>
        public static string FormatSting(string strings)
        {
            strings = Regex.Replace(strings, @"[^a-zA-Z0-9_]", "");
            strings = Regex.Replace(strings, @"^\w", t => t.Value.ToUpper());
            return strings;
        }
    }
}


